import socket
import psutil
import platform


class PortScanner:
    def __init__(self, host="127.0.0.1"):
        self.host = host

    def scan(self, port):
        """检查指定端口是否开放"""
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            sock.settimeout(1)  # 设置超时时间为1秒
            result = sock.connect_ex((self.host, port))
            return result == 0  # 如果返回值为0，表示端口开放

    def get_process_by_port(self, port):
        """获取占用指定端口的进程信息"""
        try:
            # 在 macOS 上使用 lsof 命令获取端口信息
            if platform.system() == 'Darwin':
                import subprocess
                cmd = f"lsof -i :{port} -sTCP:LISTEN -t"
                try:
                    pid = int(subprocess.check_output(
                        cmd, shell=True).decode().strip())
                    process = psutil.Process(pid)
                except (subprocess.CalledProcessError, ValueError):
                    # 如果 lsof 命令失败，尝试使用 psutil
                    return self._get_process_by_psutil(port)
            else:
                return self._get_process_by_psutil(port)

            # 获取进程详细信息
            return {
                'pid': process.pid,
                'name': process.name(),
                'exe': process.exe(),
                'status': process.status(),
                'create_time': process.create_time(),
                'username': process.username()
            }
        except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
            return {
                'pid': pid if 'pid' in locals() else None,
                'name': '无法访问',
                'exe': '权限不足',
                'status': str(e),
                'create_time': 0,
                'username': '未知'
            }
        except Exception as e:
            return {
                'pid': None,
                'name': '未知进程',
                'exe': '无法获取',
                'status': f'错误: {str(e)}',
                'create_time': 0,
                'username': '未知'
            }

    def _get_process_by_psutil(self, port):
        """使用 psutil 获取端口信息"""
        try:
            for conn in psutil.net_connections(kind='inet'):
                if hasattr(conn, 'laddr') and conn.laddr.port == port:
                    process = psutil.Process(conn.pid)
                    return {
                        'pid': process.pid,
                        'name': process.name(),
                        'exe': process.exe(),
                        'status': process.status(),
                        'create_time': process.create_time(),
                        'username': process.username()
                    }
        except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
            return {
                'pid': conn.pid if 'conn' in locals() else None,
                'name': '无法访问',
                'exe': '权限不足',
                'status': str(e),
                'create_time': 0,
                'username': '未知'
            }
        return None

    def kill_process(self, pid):
        """终止指定PID的进程"""
        try:
            process = psutil.Process(pid)
            process.terminate()  # 尝试正常终止
            try:
                process.wait(timeout=3)  # 等待进程终止
            except psutil.TimeoutExpired:
                process.kill()  # 如果等待超时，强制终止
            return True
        except psutil.NoSuchProcess:
            return False
        except psutil.AccessDenied:
            if platform.system() == 'Windows':
                # 在Windows上尝试使用管理员权限终止进程
                import ctypes
                if ctypes.windll.shell32.IsUserAnAdmin():
                    try:
                        import os
                        os.system(f'taskkill /F /PID {pid}')
                        return True
                    except:
                        return False
            return False

    def check_port_5000(self):
        """检查5000端口状态并返回相关信息"""
        if self.scan(5000):
            process_info = self.get_process_by_port(5000)
            if process_info:
                return {
                    'status': 'occupied',
                    'process': process_info
                }
            return {
                'status': 'occupied',
                'process': None  # 端口被占用但无法获取进程信息
            }
        return {
            'status': 'free'
        }
